#pragma once
#include "logMessage.hpp"
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 #include <sys/wait.h>
#include <unistd.h>
namespace server
{
    using namespace std;
    enum
    {
        USAGE_ERROR = 0,
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROR
    };
    static int backlog = 5;
    static uint16_t severport = 8080;
    class tcpserver
    {
    public:
        tcpserver(const uint16_t &port = severport)
            : _listenSockfd(-1), _port(port)
        {
        }
        void initServer()
        {
            // 1.首先创建套接字
            _listenSockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_listenSockfd == -1)
            {
                debugLog(FATAL, "socket error");
                exit(SOCKET_ERROR);
            }
            debugLog(NORMAL, "socket success");
            // 2.bind操作
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);      // 主机转网络这儿需要进行一次转换
            local.sin_addr.s_addr = INADDR_ANY; // 不需要绑定特定的端口号，所有的底层网络收到的消息都往上传

            if (bind(_listenSockfd, (struct sockaddr *)&local, sizeof(local)) <0)
            {
                debugLog(FATAL, "bind error");
                exit(BIND_ERROR);
            }
            debugLog(NORMAL, "bind success");
            // 这里tcp与udp最大的不同之处在于，tcp是需要建立链接的，也就是需要监听的
            int sockfd = listen(_listenSockfd, backlog); // 这里先把第二个参数设置为5
            // 3.这里开始进行监听操作
            if (sockfd < 0) // 建立链接失败
            {
                debugLog(FATAL, "listen error");
                exit(LISTEN_ERROR);
            }
            debugLog(NORMAL, "listen sucess");
        }

        void runServer()
        {

            for (;;)
            {
                //4.sever 获取新链接 accept函数
                struct sockaddr_in peer; 
                socklen_t len =sizeof(peer);
                //这里accept返回的sock也是一个文件描述符，通过这个与对应客户端进行链接
                int sock = accept(_listenSockfd,(struct sockaddr*)&peer,&len);
                if(sock<0)
                {
                    debugLog(ERROR,"accept sock error");
                    continue;
                }
                uint16_t clientport = peer.sin_port;
                //version 1  单进程主进程
                // 5. 这里就是一个sock，未来通信我们就用这个sock，面向字节流的，后续全部都是文件操作
                // seveceIO(sock,clientport);
                // close(sock);

                //接下来使用多进程
                pid_t id = fork();
                if(id==0)
                {
                    //child子进程
                    //对于子进程而言，需要父进程进行回收，父进程回收的话有两种方式
                    //阻塞等待和非阻塞等待，但是执行任务时死循环，如果阻塞等待跟串行运行没有区别
                    //所以选择再次fork()出一个孙子进程，再把子进程回收，然孙子进程去OS操作系统下
                    close(_listenSockfd); //对于每个子进程而言，都有父进程的资源，所以可以选择把listensockfd关闭
                    if(fork()>0) exit(-1);
                    seveceIO(sock,clientport);
                    close(sock);
                    exit(0);  //执行完成自动交给父进程退出
                }
                //father
                //waitpid(id,nullptr,WNOHANG);//阻塞式等待会变成串行执行，不能构建多个子进程
            }
        }

        static void seveceIO(const int& fd,const uint16_t& clientport)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t num = read(fd, buffer, sizeof(buffer) - 1); // 对于从网络流中读取数据，用到的也是read函数 留一个位置给/0
                if(num>0)
                {
                    //将目前渡到的数据当作字符串，截至目前
                    string recvmessage=buffer;
                    buffer[0]=0;
                    cout<<"recv message"<<"["<<clientport<<"]"<<recvmessage<<endl;

                    string outbuffer = buffer;
                    outbuffer+=" server[echo]";

                    write(fd,outbuffer.c_str(),outbuffer.size());  //进行多路转接
                }
                else if(num==0)
                {
                    //代表client退出
                    cout<<"client quit!,me too"<<endl;
                    break;
                }
            }
            close(fd);
        }
        ~tcpserver()
        {
        }

    private:
        int _listenSockfd;
        uint16_t _port;
    };

}